/*
 * GDefaultNationalAssocImpl.java
 * Created on 25 sept. 2008
 *
 * Glacéo Internet Platform
 * http://sourceforge.net/projects/chm/
 *
 * Copyright (c) 2005-2008, Jens and Jan Janke (VirtualHockey Project)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hockeo.vhbase.data.impl.assoc;

import java.util.List;
import java.util.Map;
import java.util.Random;

import com.hockeo.vhbase.conf.GRulesConstants;
import com.hockeo.vhbase.data.dao.IGDao;
import com.hockeo.vhbase.data.dao.assoc.GAssociation;
import com.hockeo.vhbase.data.dao.club.GClub;
import com.hockeo.vhbase.data.dao.club.GClubLeagueHistoryRecord;
import com.hockeo.vhbase.data.dao.contest.GLeague;
import com.hockeo.vhbase.data.dao.season.GSeason;
import com.hockeo.vhbase.data.impl.IGAssociationImpl;

/**
 * Default implementation for national associations.
 *
 * @version $Id$
 * @author jjanke
 */
public class GDefaultNationalAssocImpl implements IGAssociationImpl
{
  private IGDao d_dao;

  public final void setDao( IGDao dao )
  {
    d_dao = dao;
  }

  public void doLeaguePromotionsAndRelegations( final GAssociation assoc, final GSeason season )
  {
    GSeason seasonLast = d_dao.findLastFinishedSeason();

    // If no season has been finished so far, there is nothing to do
    //
    if ( seasonLast == null )
      return;

    Map<Integer, List<GClub>> mapPromoted = d_dao.findPromotedClubsAsMap( assoc, seasonLast );
    Map<Integer, List<GClub>> mapRelegated = d_dao.findRelegatedClubsAsMap( assoc, seasonLast );

    for ( int i = 1; i <= GRulesConstants.MAX_NUM_LEAGUE_CATEGORIES; i++ )
    {
      List<GClub> list;

      // Promotions (as from 2nd league category)
      //
      if ( i > 1 )
      {
        list = mapPromoted.get( Integer.valueOf( i ) );
        if ( list != null )
          assignClubsToNewLeagues( list, assoc, i - 1 );
      }

      // Relegations (until before-last league category)
      //
      if ( i < GRulesConstants.MAX_NUM_LEAGUE_CATEGORIES )
      {
        list = mapRelegated.get( Integer.valueOf( i ) );
        if ( list != null )
          assignClubsToNewLeagues( list, assoc, i + 1 );
      }
    }
  }

  public void undoLeaguePromotionsAndRelegations( final GAssociation assoc, final GSeason season )
  {
    GSeason seasonLast = d_dao.findLastFinishedSeason();

    // If no season has been finished so far, there is nothing to do
    //
    if ( seasonLast == null )
      return;

    List<GClub> list = d_dao.findPromotedClubs( assoc, seasonLast );
    list.addAll( d_dao.findRelegatedClubs( assoc, seasonLast ) );

    // Reassign promoted/relegated clubs to their last season league
    //
    for ( GClub club : list )
    {
      GClubLeagueHistoryRecord chr = d_dao.findClubHistoryRecord( GClubLeagueHistoryRecord.class, seasonLast, club );
      club.assignToLeague( d_dao.findEntity( GLeague.class, chr.getContest().getId() ) );
    }
  }

  public void assignClubSeedings( final GAssociation assoc, final GSeason season )
  {
    GSeason seasonLast = d_dao.findLastFinishedSeason();

    // If no season has been finished so far, there is nothing to do
    //
    if ( seasonLast == null )
      return;

    // Get all clubs that participated in a league contest last season in the new seeding
    // order
    //
    List<GClub> listClubs = d_dao.findClubsInSeedingOrder( assoc, seasonLast );
    int nSeeding = 1;

    for ( GClub club : listClubs )
      club.setSeeding( nSeeding++ );

    // Find new clubs
    //
    listClubs = d_dao.findClubsNotPlayingInLeagueContest( assoc, seasonLast );

    for ( GClub club : listClubs )
      club.setSeeding( nSeeding );
  }

  public void undoAssignClubSeedings( final GAssociation assoc, final GSeason season )
  {
    GSeason seasonBeforeLast = d_dao.findEntity( GSeason.class, season.getId() - 2 );

    // If there is no season, we reset the seedings and stop
    //
    if ( seasonBeforeLast == null )
    {
      d_dao.resetClubSeedings( assoc );
      return;
    }

    // Reset the seedings of all clubs to 0
    //
    d_dao.resetClubSeedings( assoc );

    // Get all clubs that participated in a league contest last season in the new seeding
    // order
    //
    List<GClub> listClubs = d_dao.findClubsInSeedingOrder( assoc, seasonBeforeLast );
    int nSeeding = 1;

    for ( GClub club : listClubs )
      club.setSeeding( nSeeding++ );

    // Find new clubs
    //
    listClubs = d_dao.findClubsNotPlayingInLeagueContest( assoc, seasonBeforeLast );

    for ( GClub club : listClubs )
      club.setSeeding( nSeeding );
  }

  /**
   * Assigns the specified clubs evenly to all leagues of the indicated category. If there
   * is more than one league, the teams are distributed randomly. If another distribution
   * is required, it should be carried out at the end of all promotions/relegations.
   *
   * @param listClubs the clubs that should be attached
   * @param nDestLeagueCategory
   */
  private void assignClubsToNewLeagues( List<GClub> listClubs, GAssociation assoc, int nDestLeagueCategory )
  {
    List<GLeague> listLeagues = d_dao.findLeagues( assoc, nDestLeagueCategory );

    assert listLeagues.size() > 0 : "There should always be at least one league for assoc " + assoc.getId()
        + " and category " + nDestLeagueCategory;
    int nClubsPerLeague = listClubs.size() / listLeagues.size();
    Random r = null;

    if ( listLeagues.size() > 1 )
      r = new Random();

    for ( GLeague league : listLeagues )
    {
      for ( int i = 0; i < nClubsPerLeague; i++ )
      {
        // If there is more than one league, the clubs are randomly distributed among them
        //
        if ( r != null )
        {
          int iClubIdx = r.nextInt( listClubs.size() );
          listClubs.get( iClubIdx ).assignToLeague( league );
          listClubs.remove( iClubIdx );
        }
        else
          listClubs.get( i ).assignToLeague( league );
      }
    }
  }
}
